/**
 * 创建一个音乐播放器的类 单例模式
 */
class Player {
    constructor($songList, $reload) { //类的构造函数

        //如果类已经有实例了，就返回这个实例
        if (Player.instance && !$reload) return Player.instance;
        //如果没有实例化，就去构造一个实例
        return this.getInstance($songList);

    }

    //构建实例
    getInstance($songList) {
        let instance = new PlayerCreator($songList);
        //让实例可以使用到Player的原型的属性方法
        // instance.__proto__=Player.prototype;
        // instance.constructor=Player;
        // 暂存初始[HTML]
        Player.baseHtml = $('body').html;
        //把构建好的实例挂在Player类上
        Player.instance = instance;
        this.ok = '123';
        return instance;
    }
}

/**
 * 歌曲信息
 */
class Musics {
    /**
     * 初始化
     * @param $customSongs
     */
    constructor($customSongs) {

        // 操作的歌单是字符串 JSON -> 数组
        if (typeof $customSongs === 'string') $customSongs = JSON.parse($customSongs);
        // 最终赋值成员
        this.songs = $customSongs;
    }

    //根据索引获取歌曲的方法
    getSongByNum(index) {
        return this.songs[index];
    }
}

/**
 * 真正的构建播放器的类
 */
class PlayerCreator {

    constructor($songList) {

        this.audio = document.getElementById('audio'); // Audio dom元素,
        // 因为很多api都是需要原生audio调用的，所以不用jq获取
        // this.audio.muted = true; // 控制静音
        this.audio.volume = 0.8;

        //工具
        this.util = new Util();
        this.ListeningMusics = new Musics($songList); // 正在收听歌曲列表
        this.ListeningIndex = 0; // 当前播放的歌曲索引

        this.seeingMusics = new Musics($songList); // 正在观看歌曲列表
        this.loop_mode = 0; // 0-列表循环, 1-随机, 2-单曲循环
        // 下方歌曲列表容器
        this.seeingListDom = $('.songUL');

        this.listeningDoms = { //切换歌曲时需要渲染的dom组
            title: $('.songName'), // 歌名
            singer: $('.songPlayer'), // 歌唱者
            image: $('.albumCover img'), // 封面
            blur: $('.music-player__blur')
        };
        // this.ban_dom = { //禁音时需要渲染的dom组
        //     control__btn: $('.control__volume--icon')
        // }

        // 时间显示容器
        this.render_time = {
            now: $('.nowTime'),
            total: $('.totalTime')
        };

        // 唱片
        this.disc = {
            image: $('.music-player__image'),
            pointer: $('.music-player__pointer')
        };
        //播放器初始化
        this.init($songList);
    }

    //初始化函数
    init($songList) {
        try {
            this.renderSeeing($songList);
            // 初始化不自动播放
            this.renderSongStyle(false);
            this.bindEventListener();
            // 循环更新播放器状态等信息
            this.loopUpdatePlayer();
        } catch ($err) {
            console.error('播放器初始化失败，稍后将再次尝试！');
            return setTimeout(() => {
                this.init($songList);
            }, 3000);
        }
    }

    /**
     * 生成[正在播放]列表
     * @param $customSongs 歌曲列表
     * @param $index 首先播放第几首歌曲
     */
    renderListening($customSongs, $index) {
        // 已播放不操作
        if (this.ListeningIndex === $index) return false;
        // 根据数据重新初始化
        this.ListeningMusics = new Musics($customSongs);
        // 播放歌曲
        this.changeSong($index);
    }

    /**
     * 生成正在查看[歌单列表]
     * @param $songList
     */
    renderSeeing($songList) {

        // 歌曲信息
        this.seeingMusics = new Musics($songList);
        // 如果列表为空则渲染当前列表
        $songList = this.seeingMusics.songs;

        var _str = '', itemSid,
            // 正在播放歌曲信息
            playingSong = this.ListeningMusics.getSongByNum(this.ListeningIndex),
            // 正在播放歌曲[编号] - 不这么写可能会报错
            playingId = !playingSong || !playingSong.id ? -1 : playingSong.id.toString(),
            // 正在播放歌曲[站点] - 不这么写可能会报错
            playingSite = !playingSong || !playingSong.site ? -1 : playingSong.site.toString(),
            playingSid = playingId + '_' + playingSite,
            stage = ''
        ;
        $songList.forEach((song, i) => {

            // 生成当前条目[sid]
            itemSid = song.id + '_' + song.site;

            _str += `<li class="songList ${itemSid === playingSid ? 'playing' : ''}"
                        data-id="${song.id}">
                        <div class="songLMain">
                            <div class="check">
                                <input class="checkIn" type="checkbox" select="0"
                                    value="${song.id}" data-site="${song.site}">
                            </div>
                            <div class="start">
                                <em sonN="${i + 1}">${i + 1}</em>
                            </div>
                            <div class="songBd">
                                <div class="col colsn">${song.name}</div>
                                <div class="col colcn">${song.singer_name}</div>
                                <div class="col">${song.album_name}</div>
                            </div>
                            <div class="control">
                    `;
            // 列表是[收藏列表]
            if (song.list_type !== 'collectList') {
                _str += `        <a class="cicon love collectSong ${parseInt(song.is_collected) === 1 ? 'cur' : ''}" data-id="${song.id}" data-site="${song.site}" data-status="${song.is_collected}" data-list-type="${song.list_type}"></a>`;
            }
            // 如果是歌曲有[歌单编号]添加到[data]数据
            if (song.play_list_id) stage += ' data-play-list-id="' + song.play_list_id + '"';
            _str += `            <a class="cicon more showMore" data-id="${song.id}"
                                    data-site="${song.site}" style="display:none"></a>
                                 <a class="cicon dele" style="display:none" data-id="${song.id}"
                                    data-site="${song.site}" data-list-type="${song.list_type}"
                                    ${stage}></a>
                            </div>
                        </div>
                    </li>`;
            // 还原[sid]
            itemSid = 0;
        });

        if ($w.isEmpty($songList)) {
            _str = $('#noSong').html();
        }

        this.seeingListDom.html(_str);
    }

    /**
     * 根据歌曲去渲染视图
     * @param $playSong 是否直接播放当前歌曲
     * @returns {boolean}
     */
    renderSongStyle($playSong) {
        // 当前不可播放歌曲
        this.audioLodad = false;
        if (!this.ListeningMusics.songs || this.ListeningMusics.songs.length < 1) return false;
        let {
            id,
            site
        } = this.ListeningMusics.getSongByNum(this.ListeningIndex);
        // 先加到播放按钮正在加载
        $('.playBtn').addClass('no-bg-img').html(
            '<div class="loadEffect">' +
            '   <div><span></span></div>' +
            '   <div><span></span></div>' +
            '   <div><span></span></div>' +
            '   <div><span></span></div>' +
            '</div>'
        );
        let that = this;

        $.get($w.getApiUrl('song.detail'), {
            id: id,
            site: site
        }, ($response) => {
            if (parseInt($response.no) !== 200) {
                layer.msg('该歌曲暂时无法播放，即将为您播放其他歌曲', {
                    offset: ['70%'],
                    skin: 'layer-error',
                });
                return setTimeout(() => {
                    $('.nextBtn').click();
                }, 3000);
            }

            // 播放地址
            this.audio.src = $response.data.url;
            // 收藏按钮
            $('.listeningCollect').attr('data-id', id).attr('data-site', site).addClass(
                parseInt($response.data.is_collected) === 1 ? 'cur' : ''
            ).attr('data-status', parseInt($response.data.is_collected));
            // 更多按钮
            $('.listeningMore').attr('data-id', id).attr('data-site', site);
            // 播放中[DOM] - 歌曲名
            this.listeningDoms.title.html($response.data.name);
            // 播放中[DOM] - 歌手
            this.listeningDoms.singer.html($response.data.singer_name);
            // 播放中[DOM] - 封面
            this.listeningDoms.image.prop('src', $response.data.album_cover);
            // 切换当前[播放列表]中当前歌曲 play
            this.seeingListDom.find('li[data-id="' + $response.data.id + '"]')
                .addClass('playing')
                .siblings('li[data-id!="' + $response.data.id + '"]')
                .removeClass('playing');

            // [Cookie]播放历史更新
            that.addListenedList($response.data);

            // 放开播放歌曲按钮
            $('.playBtn').removeClass('no-bg-img').html('');
            // 歌曲加载完毕
            that.audioLodad = true;
            // 第一次打开无需自动播放 其他时候如果正在播放中自动播放
            if (this.isPlaying) $('.playBtn ').click();
            // 循环赋值歌词
            var lyricHtml = '', expData,
                lyrics = ($response.data.lrc ? $response.data.lrc : [{lrc: '暂无歌词', time: '0'}]);
            for (var i in lyrics) {
                if (!lyrics.hasOwnProperty(i)) continue;
                expData = lyrics[i]['time'].split('.');
                lyricHtml +=
                    '<p data-time="' + lyrics[i]['time'] + '"' +
                    '   data-second="' + expData[0] + '" data-millisecond="' + expData[1] + '">' +
                    '   ' + lyrics[i]['lrc'] +
                    '</p>';
            }
            $('#lyr > div').html(lyricHtml);

            $(this.listeningDoms.image).load(() => {
                // 加载模糊背景 - 此方法一定要在最后
                var c = document.getElementById("canvas"),
                    ctx = c.getContext("2d"),
                    img = document.getElementById("canvas1"),
                    bodyDom = $('body'),
                    width = bodyDom.width(),
                    height = bodyDom.height()
                ;
                c.width = width;
                c.height = height;
                ctx.drawImage(img, 0, 0, width, height);
                stackBlurCanvasRGBA('canvas', 0, 0, width, height, 180);
                // 主色调
                var colorThief = new ColorThief();
                bodyDom.css({backgroundColor: 'rgb(' + colorThief.getColor(img) + ')'})
            });
        });
    }

    /**
     * 添加歌曲到[历史播放]列表
     * @param $song
     * @returns {boolean}
     */
    addListenedList($song) {

        this.ok = '1';
        var historyList = $w.getCache('historyList');

        // 空[默认赋值]下
        if ($w.isEmpty(historyList)) historyList = [];
        // 添加上当前歌曲
        var newHistoryList = [{
            id: $song.id,
            site: $song.site,
            name: $song.name,
            singer_name: $song.singer_name,
            album_name: $song.album_name,
        }];
        // 接取最后到第一，300首歌曲
        historyList = historyList.slice(
            !historyList || historyList.length < 300 ? 0 : historyList.length - 299
        );
        // 过滤掉旧歌曲中已存在的此歌曲
        for (var i in historyList) {
            if (!historyList.hasOwnProperty(i)) continue;
            if (historyList[i]['id'] === $song.id && historyList[i]['site'] === $song.site) {
                continue;
            }
            newHistoryList.push(historyList[i]);
        }
        newHistoryList = $w.array_values(newHistoryList);

        // 最终更新下缓存
        $w.setCache('historyList', newHistoryList);
        return true;
    }

    //绑定各种事件
    bindEventListener() {
        //播放按钮
        this.$play = new Btns('.playBtn', {
            click: this.handlePlayAndPause.bind(this)
        });
        //上一首
        this.$prev = new Btns('.prevBtn', {
            click: this.changeSong.bind(this, 'prev')
        });
        //下一首
        this.$next = new Btns('.nextBtn', {
            click: this.changeSong.bind(this, 'next')
        });
        //循环模式
        this.$mode = new Btns('.modeBtn', {
            click: this.changePlayMode.bind(this)
        });
        //禁音
        this.$ban = new Btns('.volBtn', {
            click: this.banNotes.bind(this)
        });
        //列表点击
        this.seeingListDom.on('click', 'li .start', (e) => {
            let index = $(e.target).parent().parent().parent().index();
            // 播放歌单是当前查看的歌单，歌曲索引是点击的索引
            this.renderListening(this.seeingMusics.songs, index);
        });
        // 列表移入
        this.seeingListDom.on('mouseover', 'li', (e) => {
            $(e['currentTarget']).find('.cicon').show();
        });
        // 列表移出
        this.seeingListDom.on('mouseout', 'li', (e) => {
            $(e['currentTarget']).find('.more').hide();
            $(e['currentTarget']).find('.dele').hide();
        });

        //音量控制 audio标签音量 vlouem 属性控制0-1
        this.volProgress = new Progress('.volControl', {
            min: 0,
            max: 1,
            value: this.audio.volume,
            handler: (value) => { //更改进度时
                this.audio.volume = value;
            }
        });


        //歌曲进度 this.audio.duration

        //可以播放的时候触发（歌曲的基本信息都已经获取到了）
        this.audio.oncanplay = () => {

            //避免重复实例化
            if (this.progress) {
                this.progress.max = this.audio.duration; //切换歌曲后更新时长
                this.render_time.total.html(this.util.formatTime(this.audio.duration));
                return false;
            }
            this.progress = new Progress('.progressBtn', {
                min: 0,
                max: this.audio.duration,
                value: 0,
                handler: (value) => {
                    this.audio.currentTime = value;
                }
            });
            //调整总时长
            this.render_time.total.html(this.util.formatTime(this.audio.duration));
        };

        //会在播放的时候持续触发
        this.audio.ontimeupdate = () => {
            if (!this.progress) this.audio.oncanplay();
            this.progress.setValue(this.audio.currentTime);
            //调整当前时长
            this.render_time.now.html(this.util.formatTime(this.audio.currentTime));

            // 渲染歌词
            this.renderLyric(this.audio.currentTime);
        };

        //当歌曲播放完成的时候
        this.audio.onended = () => {
            this.changeSong('next');
        };

        this.audio.onerror = () => {
            layer.msg('该歌曲暂时无法播放，即将为您播放其他歌曲', {
                offset: ['70%'],
                skin: 'layer-error',
            });
            console.error('歌曲播放出错！');
            return setTimeout(() => {
                this.changeSong('next');
            }, 3000);
        };
        // 当歌曲出错的时候
        // $(this.audio).onerror = () => {
        //     layer.msg('(ಥ﹏ಥ)该歌曲暂时无法播放，即将为您播放其他歌曲', {
        //         offset: ['70%'],
        //                 skin: 'layer-error',
        //     });
        //     console.error('歌曲出错！');
        //     return setTimeout(() => {
        //         this.changeSong('next');
        //     }, 3000);
        //     // 切歌即可
        // };
    }

    //播放暂停控制
    handlePlayAndPause() {

        let _o_i = this.$play.$el; // .find('i');
        //this.audio.pauseed值为true 说明目前是不播放
        if (this.audio.paused) { //现在是暂停的 要播放
            // 歌曲未加载完毕 无需播放
            if (!this.audioLodad) return false;
            // 歌曲播放
            this.audio.play();
            // 歌曲 正在播放中
            this.isPlaying = true;
            // 播放样式
            _o_i.css({
                backgroundPosition: '0px -30px'
            });
            // this.disc.image.addClass('play');
            // this.disc.pointer.addClass('play')
        } else {
            // 歌曲暂停
            this.audio.pause();
            // 歌曲 不在播放中
            this.isPlaying = false;
            // 暂停样式
            _o_i.css({
                backgroundPosition: ''
            });
            // this.disc.image.removeClass('play');
            // this.disc.pointer.removeClass('play');
        }
    }

    //更改循环模式
    changePlayMode() {
        this.loop_mode++;
        if (this.loop_mode > 2) this.loop_mode = 0;
        this.renderPlayMode();
    }

    //更改按钮样式
    renderPlayMode() {
        let list = {
            0: {
                mode: 'loop',
                css: {
                    backgroundPosition: ''
                }
            },
            1: {
                mode: 'random',
                css: {
                    backgroundPosition: '0 -221px'
                }
            },
            2: {
                mode: 'single',
                css: {
                    backgroundPosition: '0 -201px'
                }
            }
        };
        let _o_i = this.$mode.$el;
        //prop 改一些标签的自有属性 attr改一些标签的自定义属性
        _o_i.css(list[this.loop_mode]['css']);
    }

    //更改歌曲索引
    changeSongIndex($type) {

        if (typeof $type === 'number') {
            this.ListeningIndex = $type;
        } else {
            //随机播放
            let _length = this.ListeningMusics.songs.length;
            if (this.loop_mode === 0) {
                //列表循环
                this.ListeningIndex += $type === 'next' ? 1 : -1;
                if (this.ListeningIndex > _length - 1) this.ListeningIndex = 0;
                if (this.ListeningIndex < 0) this.ListeningIndex = _length - 1;
            } else if (this.loop_mode === 1) {
                let _random = Math.floor(Math.random() * _length);
                for (let i = 0; i < 10000; i++) { //随机的数为本身则继续随机
                    if (this.ListeningIndex === _random) {
                        _random = Math.floor(Math.random() * _length);
                    } else {
                        this.ListeningIndex = _random;
                        break;
                    }
                }
            } else if (this.loop_mode === 2) {
                // 单曲循环不操作 还是本首歌曲
                this.ListeningIndex = this.ListeningIndex;
            }
        }
    }

    //歌曲时长
    songTime() {
        let totalMinute = parseInt(this.audio.duration / 60) < 10 ? "0" + parseInt(this.audio.duration / 60) : parseInt(this.audio.duration / 60);
        let totalSecond = parseInt(this.audio.duration % 60) < 10 ? "0" + parseInt(this.audio.duration % 60) : parseInt(this.audio.duration % 60);
        $('.totalTime').text(totalMinute + ':' + totalSecond);
    }

    /**
     * 切换歌曲
     * @param $type
     */
    changeSong($type) {
        //更改索引
        this.changeSongIndex($type);
        //记录切歌前的状态
        // let _is_pause = this.audio.paused;
        //切歌后更改视图显示
        this.renderSongStyle(true);
    }

    //禁音
    banNotes() {
        let _o_i = this.$ban.$el;
        if (this.audio.muted === true) { //如果禁音则开启
            this.audio.muted = false;
            _o_i.css({
                backgroundPosition: ''
            });
            $('.volControl .back').css({
                background: ''
            });
        } else {
            this.audio.muted = true;
            _o_i.css({
                backgroundPosition: '0px -313px'
            });
            $('.volControl .back').css({
                background: 'rgb(142,142,142)'
            });
        }
    }

    /**
     * 根据当前时间渲染歌曲歌词
     * @param $currentTime
     */
    renderLyric($currentTime) {

        var expTime = ($currentTime.toString() || this.audio.currentTime).split('.'),
            baseDom = $('#lyr'), itemDom = baseDom.find('div > p[data-second="' + expTime[0] + '"]');
        // 当前歌词 颜色改变
        itemDom.addClass('cur').siblings().removeClass('cur');
        // [dom]高度
        var heightOfParents = undefined;
        if (itemDom[0]) heightOfParents = itemDom[0].offsetTop;
        if (heightOfParents !== undefined) {
            var scroll = itemDom.index() * itemDom[0]['clientHeight'];
            baseDom.find('div').css({
                transition: 'transform 1s ease-out 0s',
                transform: 'translateY(-' + (
                    scroll > 0 ? scroll - (baseDom.height() / 2) + (itemDom[0]['clientHeight'] / 2) : 0
                ) + 'px)'
            });
        }
    }

    /**
     * 持续更新播放器信息
     */
    loopUpdatePlayer() {

        var that = this;
        let _o_i = this.$play.$el;
        setInterval(function () {

            // 渲染下当前歌曲状态并且更新
            // 目前是不播放
            if (this.audio.paused) {
                // 暂停样式
                _o_i.css({
                    backgroundPosition: ''
                });
            } else {
                // 播放样式
                _o_i.css({
                    backgroundPosition: '0px -30px'
                });
            }
        }, 3000);
    }
}

/**
 * 进度条
 */
class Progress {
    constructor(selector, options) {
        $.extend(this, options);
        ///给this挂载传入的参数
        this.$el = $(selector);
        this.width = this.$el.width();
        this.init();
    }

    //进度条初始化
    init() {
        this.renderBackAndPointer();
        //this.bindEvents();
        this.drag();
        this.value;
        this.changeDOMStyle(this.width * this.value);
    }

    //为进度条渲染back和pointer
    renderBackAndPointer() {
        this.$back = $('<div class="back">');
        this.$pointer = $('<div class="pointer dian">');

        this.$el.append(this.$back);
        this.$el.append(this.$pointer);
    }

    setValue(value) { //主动调用，传入value值，设置进度条样式
        let _distance = this.width * value / (this.max - this.min);
        this.changeDOMStyle(_distance);
    }

    drag() {
        let ele = this.$el;
        let flag = false; //鼠标是否点击
        ele.click((e) => {
            flag = true;
            let _left = e.offsetX;
            let _distance = ele.outerWidth(false) - (ele.outerWidth(false) - _left);

            let _ratio = _distance / ele.outerWidth(false);
            let _value = _ratio * (this.max - this.min); //当前的音量值
            this.changeDOMStyle(_distance);
            this.handler(_value); //更改进度之后，执行回调
        });
    }

    bindEvents() { //鼠标点击时更改
        this.$el.click((e) => {
            let _x = e.offsetX; //鼠标距离元素左边的距离
            let _ratio = _x / this.width;
            let _value = _ratio * (this.max - this.min); //当前的音量值
            this.changeDOMStyle(_x);
            this.handler(_value); //更改进度之后，执行回调
        })
    }

    //更改pointer和back
    changeDOMStyle(distance) {
        this.$back.width(distance + 7 === 7 ? 0 : distance + 7);//进度为0时将进度条背景改为0否则加上进度按钮的长度
        this.$pointer.css('left', distance + 'px');
    }
}

/**
 * 按钮类
 */
class Btns {
    constructor(selector, handlers) {
        this.$el = $(selector); //元素
        this.bindEvents(handlers);
    }

    bindEvents(handlers) { //绑定事件
        for (const event in handlers) {
            //使用值的时候保证键值对在对象中是存在的
            if (handlers.hasOwnProperty(event)) {
                this.$el.on(event, handlers[event]);
            }
        }
    }
}
